<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>第14章 实战：使用 Webpack 或 Vue CLI 搭建多页应用 | 深入理解Vue.js实战</title>
    <meta name="generator" content="VuePress 1.9.7">
    
    <meta name="description" content="作者：被删">
    
    <link rel="preload" href="/vue-ebook/assets/css/0.styles.2728a2da.css" as="style"><link rel="preload" href="/vue-ebook/assets/js/app.a856ee15.js" as="script"><link rel="preload" href="/vue-ebook/assets/js/2.2a4d101b.js" as="script"><link rel="preload" href="/vue-ebook/assets/js/3.9083e877.js" as="script"><link rel="preload" href="/vue-ebook/assets/js/13.8d233329.js" as="script">
    <link rel="stylesheet" href="/vue-ebook/assets/css/0.styles.2728a2da.css">
  </head>
  <body>
    <div id="app" data-server-rendered="true"><div class="theme-container"><header class="navbar"><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="/vue-ebook/" class="home-link router-link-active"><!----> <span class="site-name">深入理解Vue.js实战</span></a> <div class="links"><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <!----></div> <nav class="nav-links can-hide"><div class="nav-item"><a href="/vue-ebook/" class="nav-link">概述</a></div><div class="nav-item"><a href="/vue-ebook/vue-ebook/" class="nav-link router-link-active">内容</a></div> <a href="https://github.com/godbasin/vue-ebook" target="_blank" rel="noopener noreferrer" class="repo-link">
    Github
    <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></nav></div></header> <div class="sidebar-mask"></div> <aside class="sidebar"><nav class="nav-links"><div class="nav-item"><a href="/vue-ebook/" class="nav-link">概述</a></div><div class="nav-item"><a href="/vue-ebook/vue-ebook/" class="nav-link router-link-active">内容</a></div> <a href="https://github.com/godbasin/vue-ebook" target="_blank" rel="noopener noreferrer" class="repo-link">
    Github
    <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></nav>  <ul class="sidebar-links"><li><section class="sidebar-group depth-0" style="padding-top:;"><!----> <p class="sidebar-heading"><span>前言</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="/vue-ebook/vue-ebook/0.html" class="sidebar-link">前端框架的出现</a></li></ul></section></li><li><section class="sidebar-group depth-0" style="padding-top:;"><!----> <p class="sidebar-heading"><span>第一部分 Vue快速入门</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="/vue-ebook/vue-ebook/1.html" class="sidebar-link">第1章 Vue 框架介绍</a></li><li><a href="/vue-ebook/vue-ebook/2.html" class="sidebar-link">第2章 Vue 环境快速搭建</a></li><li><a href="/vue-ebook/vue-ebook/3.html" class="sidebar-link">第3章 Vue 基础介绍</a></li><li><a href="/vue-ebook/vue-ebook/4.html" class="sidebar-link">第4章 Vue 组件的使用</a></li><li><a href="/vue-ebook/vue-ebook/5.html" class="sidebar-link">第5章 常用指令和自定义指令</a></li><li><a href="/vue-ebook/vue-ebook/6.html" class="sidebar-link">第6章 Vue 动画</a></li><li><a href="/vue-ebook/vue-ebook/7.html" class="sidebar-link">第7章 Vue Router 路由搭建应用</a></li><li><a href="/vue-ebook/vue-ebook/8.html" class="sidebar-link">第8章 实战：Todo List 从组件到应用</a></li></ul></section></li><li><section class="sidebar-group depth-0" style="padding-top:10px;"><div class="kitty-main" data-v-2b653b36><span class="stand" data-v-2b653b36></span> <div class="cat" data-v-2b653b36><div class="body" data-v-2b653b36></div> <div class="head" data-v-2b653b36><div class="ear" data-v-2b653b36></div> <div class="ear" data-v-2b653b36></div></div> <div class="face" data-v-2b653b36><div class="nose" data-v-2b653b36></div> <div class="whisker-container" data-v-2b653b36><div class="whisker" data-v-2b653b36></div> <div class="whisker" data-v-2b653b36></div></div> <div class="whisker-container" data-v-2b653b36><div class="whisker" data-v-2b653b36></div> <div class="whisker" data-v-2b653b36></div></div></div> <div class="tail-container" data-v-2b653b36><div class="tail" data-v-2b653b36><div class="tail" data-v-2b653b36><div class="tail" data-v-2b653b36><div class="tail" data-v-2b653b36><div class="tail" data-v-2b653b36><div class="tail" data-v-2b653b36><div class="tail" data-v-2b653b36></div></div></div></div></div></div></div></div></div></div> <p class="sidebar-heading open"><span>第二部分 Vue的正确使用方式</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="/vue-ebook/vue-ebook/9.html" class="sidebar-link">第9章 思维转变与大型项目管理</a></li><li><a href="/vue-ebook/vue-ebook/10.html" class="sidebar-link">第10章 如何正确地进行抽象</a></li><li><a href="/vue-ebook/vue-ebook/11.html" class="sidebar-link">第11章 全局数据管理与 Vuex</a></li><li><a href="/vue-ebook/vue-ebook/12.html" class="sidebar-link">第12章 实战：三天开发一个管理端</a></li><li><a href="/vue-ebook/vue-ebook/13.html" class="sidebar-link">第13章 实战：表单配置化实现</a></li><li><a href="/vue-ebook/vue-ebook/14.html" aria-current="page" class="active sidebar-link">第14章 实战：使用 Webpack 或 Vue CLI 搭建多页应用</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/vue-ebook/vue-ebook/14.html#_14-1-多页应用的区别" class="sidebar-link">14.1 多页应用的区别</a></li><li class="sidebar-sub-header"><a href="/vue-ebook/vue-ebook/14.html#_14-2-webpack-实现多应用共享项目" class="sidebar-link">14.2 Webpack 实现多应用共享项目</a></li><li class="sidebar-sub-header"><a href="/vue-ebook/vue-ebook/14.html#_14-3-vue-cli-脚手架配置" class="sidebar-link">14.3 Vue CLI 脚手架配置</a></li></ul></li><li><a href="/vue-ebook/vue-ebook/15.html" class="sidebar-link">第15章 Vue 周边拓展</a></li><li><a href="/vue-ebook/vue-ebook/16.html" class="sidebar-link">第16章 关于 Vue 3.0</a></li></ul></section></li><li><section class="sidebar-group depth-0" style="padding-top:;"><!----> <p class="sidebar-heading"><span>后记</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="/vue-ebook/vue-ebook/99.html" class="sidebar-link">关于框架选型</a></li></ul></section></li></ul> </aside> <main class="page"> <div class="theme-default-content content__default"><h1 id="第14章-实战-使用-webpack-或-vue-cli-搭建多页应用"><a href="#第14章-实战-使用-webpack-或-vue-cli-搭建多页应用" class="header-anchor">#</a> 第14章 实战：使用 Webpack 或 Vue CLI 搭建多页应用</h1> <blockquote><p>本章节相关代码存放在<a href="https://github.com/godbasin/vue-ebook/tree/vue-sourcecode/14" target="_blank" rel="noopener noreferrer">Github<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>中。</p></blockquote> <p>我们在<a href="/vue-ebook/vue-ebook/2.html">《第2章 Vue 环境快速搭建》</a>中简单介绍过 Webpack 的基本概念，以及 Vue CLI 的基本使用方式。本章会介绍如何使用 Webpack 或者 Vue CLI 搭建生成多页应用的环境。</p> <h2 id="_14-1-多页应用的区别"><a href="#_14-1-多页应用的区别" class="header-anchor">#</a> 14.1 多页应用的区别</h2> <p>单页应用这个概念，是随着前几年 AngularJS、React、Ember 等这些框架的出现而出现的。在前面的前言内容里，我们在页面渲染中讲了页面的局部刷新，而单页应用则是使用了页面的局部刷新的能力，在切换页面的时候刷新页面内容，从而获取更好的体验。</p> <h3 id="_14-1-1-spa-与-mpa"><a href="#_14-1-1-spa-与-mpa" class="header-anchor">#</a> 14.1.1 SPA 与 MPA</h3> <p>先从总体来看，单页应用（SinglePage Web Application，简称 SPA）和多页应用（MultiPage Application，简称 MPA）的区别如下：</p> <p>表 14-1 单页应用与多页应用的区别</p> <table><thead><tr><th>-</th> <th>SPA</th> <th>MPA</th></tr></thead> <tbody><tr><td>组成</td> <td>一个主页面 + 多个页面片段</td> <td>多个完整页面</td></tr> <tr><td>资源共用(css,js)</td> <td>共用的资源只需要加载一次</td> <td>每个页面都需要加载公用的资源</td></tr> <tr><td>url 模式</td> <td>xxx/#/page1<br>xxx/#/page2</td> <td>xxx/page1.html<br>xxx/page2.html</td></tr> <tr><td>刷新方式</td> <td>页面局部刷新或更改</td> <td>整页刷新</td></tr> <tr><td>页面跳转</td> <td>外壳不变，更新局部页面内容，容易实现跳转动画</td> <td>从一个页面跳转到另一个页面，无法实现跳转动画</td></tr> <tr><td>用户体验</td> <td>页面片段间切换快，用户体验好</td> <td>页面切换需要重新加载，比较慢且流畅度低，用户体验较差</td></tr> <tr><td>数据传递</td> <td>同一个页面，全局变量等很容易实现</td> <td>依赖 url 传参、或者 cookie 、localStorage 等，实现麻烦</td></tr> <tr><td>搜索引擎优化(SEO)</td> <td>实现较为困难，不利于 SEO 检索</td> <td>实现方法简单</td></tr> <tr><td>适用场景</td> <td>对体验要求高的应用</td> <td>需要对搜索引擎友好的应用</td></tr></tbody></table> <p>可以看到的是，单页应用对 SEO 的支持依然会比多页应用要差，但是单页应用的页面体验会比多页应用好很多。</p> <h2 id="_14-2-webpack-实现多应用共享项目"><a href="#_14-2-webpack-实现多应用共享项目" class="header-anchor">#</a> 14.2 Webpack 实现多应用共享项目</h2> <p>一些 Webpack 的基本概念，我们已经在<a href="/vue-ebook/vue-ebook/2.html">第2章</a>中介绍过，主要包括入口(entry)、输出(output)、loader、插件(plugins)等等，这里就不再重复介绍了。Webpack 相关的能力其实远不止这些，感兴趣的小伙伴可以上官网或者查一些相关的文章进行查看和阅读。</p> <p>这里我们要实现一个多应用共享项目，指的是多个单页应用共享一个项目（共享公共组件、公共库、构建工具和资源），而我们需要针对每个应用/页面进行单独构建打包。</p> <h3 id="_14-2-1-项目配置"><a href="#_14-2-1-项目配置" class="header-anchor">#</a> 14.2.1 项目配置</h3> <p>多应用项目具体是怎样的组织结构，首先我们得设计一下想要的目录组织，然后再根据这样的方式来看看具体的实现方式。</p> <h4 id="目录组织"><a href="#目录组织" class="header-anchor">#</a> 目录组织</h4> <p>我们想要的目录结构，应该是根据页面 Page 进行单独打包，但其他会有共享的一些资源（组件、工具库、静态资源等），同时每个应用自身也可以用于一些应用内的公共组件和公共库等，所以我们可以设计目录组织如下：</p> <div class="language-cmd extra-class"><pre class="language-text"><code>├── build/                      # webpack配置参数文件
│   └── ...
├── src/                        # 项目代码入口
│   │
│   ├── components              # 多个项目共享的组件
│   ├── utils                   # 多个项目共享的工具库
│   ├── assets                  # 多个项目共享的静态资源
│   │
│   └── pages                   # 多个项目页面划分
│       ├── page1/              # 第一个页面或者应用
│       │   ├── main.js         # 页面/应用入口文件
│       │   ├── components      # 该页面/应用自身的组件
│       │   ├── utils           # 该页面/应用自身的工具库
│       │   ├── main.js         # 页面/应用入口文件
│       │   └── ...
│       └── page2/              # 第二个页面或者应用
│       │   ├── main.js         # 页面/应用入口文件
│       │   └── ...
│       └── pageN/              # 第N个页面或者应用
│           ├── main.js         # 页面/应用入口文件
│           └── ...
├── dist/                       # 项目打包代码
│   ├── page1/                  # 第一个页面或者应用
│   │   ├── [hash].js
│   │   └── index.html          # 页面/应用入口文件
│   ├── page2/                  # 第二个页面或者应用
│   │   ├── [hash].js
│   │   └── index.html          # 页面/应用入口文件
│   └── pageN/                  # 第N个页面或者应用
│       ├── [hash].js
│       └── index.html          # 页面/应用入口文件
├── .babelrc                    # babel编译参数
├── index.html                  # 主页模板，所有的页面共用该index.html入口
└── package.json                # 项目文件，记载着一些命令和依赖还有简要的项目描述信息
</code></pre></div><p>这里我们可以看到我们项目代码的入口位于<code>src</code>文件夹，并且每个页面或者 app 都以目录名为页面的名字。而打包后的文件也一样，以目录为单位，支持单个打包或是全部打包。</p> <h4 id="基本配置文件"><a href="#基本配置文件" class="header-anchor">#</a> 基本配置文件</h4> <p>由于我们需要实现开发时多页面共同启动，打包时分块打包的功能，故在不同环境下我们的入口<code>entry</code>和<code>plugins</code>等将会不一致，这里我们先省略：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// 这是一个常用的 Webpack 配置</span>
<span class="token keyword">var</span> path <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">&quot;path&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 配置包括入口(entry)、输出(output)、loader、插件(plugins)等</span>
<span class="token keyword">var</span> webpackConfig <span class="token operator">=</span> <span class="token punctuation">{</span>
  <span class="token literal-property property">entry</span><span class="token operator">:</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token literal-property property">output</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token literal-property property">path</span><span class="token operator">:</span> path<span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span>__dirname<span class="token punctuation">,</span> <span class="token string">&quot;dist&quot;</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token literal-property property">filename</span><span class="token operator">:</span> <span class="token string">&quot;./[hash].js&quot;</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token literal-property property">resolve</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token literal-property property">extensions</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">&quot;.js&quot;</span><span class="token punctuation">,</span> <span class="token string">&quot;.json&quot;</span><span class="token punctuation">]</span> <span class="token comment">// '.ts' and more</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token literal-property property">module</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token comment">// 一些常用 loader</span>
    <span class="token literal-property property">rules</span><span class="token operator">:</span> <span class="token punctuation">[</span>
      <span class="token punctuation">{</span>
        <span class="token literal-property property">test</span><span class="token operator">:</span> <span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">\.js$</span><span class="token regex-delimiter">/</span></span><span class="token punctuation">,</span>
        <span class="token literal-property property">loader</span><span class="token operator">:</span> <span class="token string">&quot;babel-loader&quot;</span><span class="token punctuation">,</span>
        <span class="token literal-property property">include</span><span class="token operator">:</span> <span class="token punctuation">[</span>path<span class="token punctuation">.</span><span class="token function">resolve</span><span class="token punctuation">(</span>__dirname<span class="token punctuation">,</span> <span class="token string">&quot;./src&quot;</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
      <span class="token punctuation">}</span>
      <span class="token comment">// more loaders...</span>
    <span class="token punctuation">]</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token literal-property property">plugins</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

module<span class="token punctuation">.</span>exports <span class="token operator">=</span> webpackConfig<span class="token punctuation">;</span>
</code></pre></div><h3 id="_14-2-2-获取目录名"><a href="#_14-2-2-获取目录名" class="header-anchor">#</a> 14.2.2 获取目录名</h3> <p>既然目录名字会在我们的项目搭建中起这么重要的作用，这里我们就将它们获取存起来。</p> <h4 id="使用-glob-模块"><a href="#使用-glob-模块" class="header-anchor">#</a> 使用 glob 模块</h4> <p>这里我们将使用<a href="https://www.npmjs.com/package/glob" target="_blank" rel="noopener noreferrer"><code>glob</code>模块<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>，它允许你使用<code>*</code>等符号，来写一个<code>glob</code>规则，像在 shell 脚本里一样，获取匹配对应规则的文件。</p> <p><strong>(1) 安装依赖。</strong></p> <div class="language-cmd extra-class"><pre class="language-text"><code>npm i glob
</code></pre></div><p><strong>(2) 使用方式。</strong></p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">var</span> glob <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">&quot;glob&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// options可选</span>
<span class="token function">glob</span><span class="token punctuation">(</span><span class="token string">&quot;**/*.js&quot;</span><span class="token punctuation">,</span> options<span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">er<span class="token punctuation">,</span> files</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token comment">// files是匹配到文件的文件名数组</span>
  <span class="token comment">// 如果 `nonull` 选项被设置为true，而且没有找到任何文件</span>
  <span class="token comment">// 那么files就是glob规则本身，而不是空数组</span>
  <span class="token comment">// er是当寻找的过程中遇的错误</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre></div><p>我们来看一下 glob 模块有哪些匹配规则（如果熟悉正则的你，相信也对这些规则了如指掌了）：</p> <p>表 14-2 glob 模块规则说明</p> <table><thead><tr><th>规则</th> <th>说明</th></tr></thead> <tbody><tr><td><code>*</code></td> <td>匹配该路径段中 0 个或多个任意字符</td></tr> <tr><td><code>?</code></td> <td>匹配该路径段中 1 个任意字符</td></tr> <tr><td><code>[...]</code></td> <td>匹配该路径段中在指定范围内字符</td></tr> <tr><td><code>*(pattern|pattern|pattern)</code></td> <td>匹配括号中多个模型的 0 个或多个或任意个的组合</td></tr> <tr><td><code>!(pattern|pattern|pattern)</code></td> <td>匹配不包含任何模型</td></tr> <tr><td><code>?(pattern|pattern|pattern)</code></td> <td>匹配多个模型中的 0 个或任意 1 个</td></tr> <tr><td><code>+(pattern|pattern|pattern)</code></td> <td>匹配多个模型中的 1 个或多个</td></tr> <tr><td><code>@(pattern|pattern|pattern)</code></td> <td>匹配多个模型中的任意 1 个</td></tr> <tr><td><code>**</code></td> <td>和<code>*</code>一样，可以匹配任何内容，但<code>**</code>不仅匹配路径中的某一段，而且可以匹配<code>'a/b/c'</code>这样带有<code>'/'</code>的内容，所以它还可以匹配子文件夹下的文件</td></tr></tbody></table> <p>我们的期望是，解析目录结构，获取到目录名字，然后提供给其他模块使用。这是一个相对通用的能力，所以我们新建一个工具文件，然后将公共的方法管理起来。</p> <h4 id="utils"><a href="#utils" class="header-anchor">#</a> utils</h4> <p>我们把这块获取目录名的功能作为工具单独管理起来，放在<code>build/utils.js</code>文件里，我们来看一下具体的实现：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// build/utils.js文件</span>
<span class="token keyword">var</span> glob <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">&quot;glob&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">function</span> <span class="token function">getEntries</span><span class="token punctuation">(</span><span class="token parameter">globPath</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token comment">// 获取所有匹配文件的文件名数组</span>
  <span class="token keyword">var</span> files <span class="token operator">=</span> glob<span class="token punctuation">.</span><span class="token function">sync</span><span class="token punctuation">(</span>globPath<span class="token punctuation">)</span><span class="token punctuation">,</span>
    entries <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>

  files<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">filepath</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// 取倒数第二层(view下面的文件夹)做包名</span>
    <span class="token keyword">var</span> split <span class="token operator">=</span> filepath<span class="token punctuation">.</span><span class="token function">split</span><span class="token punctuation">(</span><span class="token string">&quot;/&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">var</span> name <span class="token operator">=</span> split<span class="token punctuation">[</span>split<span class="token punctuation">.</span>length <span class="token operator">-</span> <span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">;</span>

    <span class="token comment">// 保存{'目录名': '目录路径'}</span>
    entries<span class="token punctuation">[</span>name<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token string">&quot;./&quot;</span> <span class="token operator">+</span> filepath<span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">return</span> entries<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token comment">// 获取所有匹配src下目录的文件夹名字，其中文件夹里main.js为页面入口</span>
<span class="token keyword">var</span> entries <span class="token operator">=</span> <span class="token function">getEntries</span><span class="token punctuation">(</span><span class="token string">&quot;src/**/main.js&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span>
  <span class="token literal-property property">entries</span><span class="token operator">:</span> entries
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre></div><p>该方法最终返回获取到的目录名，我们就可以根据这些目录名来进行打包了。</p> <h3 id="_14-2-3-相关-npm-模块介绍"><a href="#_14-2-3-相关-npm-模块介绍" class="header-anchor">#</a> 14.2.3 相关 Npm 模块介绍</h3> <p>打包页面需要用到一些 npm 模块（需单独安装），这里我们简单介绍一下。</p> <h4 id="ora-模块"><a href="#ora-模块" class="header-anchor">#</a> <a href="https://www.npmjs.com/package/ora" target="_blank" rel="noopener noreferrer">ora 模块<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></h4> <p>ora 模块主要用来实现 Node.js 命令行环境的 loading 效果，和显示各种状态的图标等。这里由于我们需要自行使用 Webpack 实现构建和打包能力，所以相应的一些终端输出和进度展示还是需要的，我们看一下简单的示例：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">const</span> ora <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">&quot;ora&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 开始显示</span>
<span class="token keyword">const</span> spinner <span class="token operator">=</span> <span class="token function">ora</span><span class="token punctuation">(</span><span class="token string">&quot;Loading unicorns&quot;</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">start</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token function">setTimeout</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token comment">// 一秒后设置颜色和内容</span>
  spinner<span class="token punctuation">.</span>color <span class="token operator">=</span> <span class="token string">&quot;yellow&quot;</span><span class="token punctuation">;</span>
  spinner<span class="token punctuation">.</span>text <span class="token operator">=</span> <span class="token string">&quot;Loading rainbows&quot;</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token number">1000</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre></div><h4 id="rimraf-模块"><a href="#rimraf-模块" class="header-anchor">#</a> <a href="https://www.npmjs.com/package/rimraf" target="_blank" rel="noopener noreferrer">rimraf 模块<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></h4> <p>rimraf 模块用于实现 Node.js 环境的 UNIX 命令<code>rm -rf</code>。一般来说，如果是简单的脚本我们也可以直接使用 shell 来实现，或者我们也可以使用一个 shelljs 的工具模块（一个 Node.js 环境的<code>Unix shell</code>命令），最终选择看开发者个人偏好。同样的，这里我们也看一下简单的使用方式：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token function">rimraf</span><span class="token punctuation">(</span>f<span class="token punctuation">,</span> <span class="token punctuation">[</span>opts<span class="token punctuation">]</span><span class="token punctuation">,</span> callback<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre></div><p>其中，一些参数可以参考以下表格：</p> <p>表 14-3 rimraf 模块参数说明</p> <table><thead><tr><th>参数名</th> <th>说明</th></tr></thead> <tbody><tr><td><code>f</code></td> <td>可为<code>glob</code>匹配规则的文件</td></tr> <tr><td><code>[opts]</code></td> <td>一些选项，具体可参考<a href="https://www.npmjs.com/package/rimraf" target="_blank" rel="noopener noreferrer">官方说明<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></td></tr> <tr><td><code>callback</code></td> <td>若执行过程中出错，则回调参数为<code>error</code></td></tr></tbody></table> <h4 id="chalk-模块"><a href="#chalk-模块" class="header-anchor">#</a> <a href="https://www.npmjs.com/package/chalk" target="_blank" rel="noopener noreferrer">chalk 模块<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></h4> <p>chalk 模块用于命令行输出各种样式的字符串。这里可以结合前面的 ora 模块，一起输出相应的进度状态，同时还可以设计输出的文字样式，用来做一些成功或是错误的提示。使用方式为<code>chalk.&lt;style&gt;[.&lt;style&gt;...](string, [string...])</code>，例如：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// 例如，红色带下划线的粗体字</span>
chalk<span class="token punctuation">.</span>red<span class="token punctuation">.</span>bold<span class="token punctuation">.</span><span class="token function">underline</span><span class="token punctuation">(</span><span class="token string">&quot;Hello&quot;</span><span class="token punctuation">,</span> <span class="token string">&quot;world&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre></div><h3 id="_14-2-4-node-js-模块"><a href="#_14-2-4-node-js-模块" class="header-anchor">#</a> 14.2.4 Node.js 模块</h3> <p>前面介绍了一些需要单独安装和引入的模块库，这里我们介绍将使用到的 Node.js 自带 API 和内置模块（无需安装）。</p> <h4 id="path-模块"><a href="#path-模块" class="header-anchor">#</a> <a href="https://nodejs.org/api/path.html" target="_blank" rel="noopener noreferrer">path 模块<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></h4> <p>path 模块提供了一些工具函数，用于处理文件与目录的路径，这是 Node.js 一个自带的模块。path 模块的默认操作会根据 Node.js 应用程序运行的操作系统的不同而变化。比如，当运行在<code>Windows</code>操作系统上时，path 模块会认为使用的是<code>Windows</code>风格的路径。我们来看下常用的方法：</p> <p>表 14-4 path 模块方法说明</p> <table><thead><tr><th>方法</th> <th>介绍</th></tr></thead> <tbody><tr><td><code>path.join([...paths])</code></td> <td>使用平台特定的分隔符把全部给定的<code>path</code>片段连接到一起，并规范化生成的路径。例如<code>path.join(__dirname, 'src')</code></td></tr> <tr><td><code>path.parse(path)</code></td> <td>返回一个对象，对象的属性表示<code>path</code>的元素。返回属性包括：<code>dir</code>, <code>root</code>, <code>base</code>, <code>name</code>, <code>ext</code></td></tr> <tr><td><code>path.format(pathObject)</code></td> <td>会从一个对象返回一个路径字符串，与<code>path.parse()</code>相反</td></tr> <tr><td><code>path.dirname(path)</code></td> <td>返回一个<code>path</code>的目录名，类似于<code>Unix</code>中的<code>dirname</code>命令</td></tr></tbody></table> <p>通过这个模块，我们就可以直接实现想要的文件处理和代码打包，不需要考虑兼容性了。</p> <h4 id="process-对象"><a href="#process-对象" class="header-anchor">#</a> <a href="https://nodejs.org/api/process.html" target="_blank" rel="noopener noreferrer">process 对象<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></h4> <p>process 对象是一个<code>global</code>（全局变量），提供有关信息，控制当前 Node.js 进程。作为一个对象，它对于 Node.js 应用程序始终是可用的，故无需使用<code>require()</code>。该模块的属性一般会包括：</p> <p>表 14-5 process 对象属性介绍</p> <table><thead><tr><th>属性</th> <th>介绍</th></tr></thead> <tbody><tr><td><code>process.execPath</code></td> <td>返回启动 Node.js 进程的可执行文件所在的绝对路径</td></tr> <tr><td><code>process.argv</code></td> <td><code>process.argv</code>属性返回一个数组，这个数组包含了启动 Node.js 进程时的命令行参数。第一个元素为<code>process.execPath</code>。如果需要获取<code>argv[0]</code>的值请参见<code>process.argv0</code>。第二个元素为当前执行的 JavaScript 文件路径，剩余的元素为其他命令行参数</td></tr> <tr><td><code>process.env</code></td> <td><code>process.env</code>属性返回一个包含用户环境信息的对象。</td></tr> <tr><td>像我们经常看到生产环境<code>process.env.NODE_ENV = 'production'</code>和开发环境<code>process.env.NODE_ENV = 'dev'</code></td> <td></td></tr> <tr><td><code>process.stdin</code></td> <td>输入流</td></tr> <tr><td><code>process.stdout</code></td> <td>输出流</td></tr></tbody></table> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// 示例</span>
<span class="token comment">// 运行以下命令，启动进程：</span>
$ node process<span class="token operator">-</span>args<span class="token punctuation">.</span>js one two<span class="token operator">=</span>three four

<span class="token comment">// process.argv 将输出：</span>
<span class="token number">0</span><span class="token operator">:</span> <span class="token operator">/</span>usr<span class="token operator">/</span>local<span class="token operator">/</span>bin<span class="token operator">/</span>node
<span class="token number">1</span><span class="token operator">:</span> <span class="token operator">/</span>Users<span class="token operator">/</span>mjr<span class="token operator">/</span>work<span class="token operator">/</span>node<span class="token operator">/</span>process<span class="token operator">-</span>args<span class="token punctuation">.</span>js
<span class="token number">2</span><span class="token operator">:</span> one
<span class="token number">3</span><span class="token operator">:</span> two<span class="token operator">=</span>three
<span class="token number">4</span><span class="token operator">:</span> four
</code></pre></div><h3 id="_14-2-5-打包实现"><a href="#_14-2-5-打包实现" class="header-anchor">#</a> 14.2.5 打包实现</h3> <p>好了，介绍了那么多的 Npm 模块和 Node.js 自带模块，我们来看一下这些模块功能要怎么配合实现想要的效果。其实在前端工程化的路上走，这些模块会成为常用的一些基本知识，不管是实现简单的打包构建，还是实现持续集成、自动化测试等能力，都是不可少的一些概念，在空闲的时候可以多自行学习和研究下，对个人的发展也是有不少好处的。</p> <h4 id="逻辑思路"><a href="#逻辑思路" class="header-anchor">#</a> 逻辑思路</h4> <p>不多说，我们来规划一下最终打包能实现的效果：<br>
(1) 可输入目录名，来只打包对应的页面。<br>
(2) 不输入目录名的时候，则将全部页面重新打包。</p> <p>简单来说，就是：</p> <ul><li>输入<code>npm run build page1</code>时，打包 page1 页面</li> <li>输入<code>npm run build page1 page2</code>时，打包 page1 和 page2 页面</li> <li>输入<code>npm run build</code>时，打包所有页面</li></ul> <p>前面也介绍了一些基本的 Npm 和 Node.js 模块能力，这里我们可以通过<code>process.argv</code>获取命令行参数。同时我们需要针对每个页面单独打包，这里我们将多个页面拆分成多个并行的任务，每个任务需要设置以下内容：</p> <ul><li><code>entry</code>：设置单个页面入口</li> <li><code>output.path</code>：设置最终生成文件目录</li> <li><code>plugins</code>：设置打包后的 index.html，这里我们使用相同的 index.html 作为模板</li></ul> <h4 id="代码实现"><a href="#代码实现" class="header-anchor">#</a> 代码实现</h4> <p>我们的页面打包代码放置在<code>build</code>文件夹下的<code>build.js</code>，则我们的<code>package.json</code>中的<code>script</code>：</p> <div class="language-json extra-class"><pre class="language-json"><code><span class="token punctuation">{</span>
  <span class="token property">&quot;scripts&quot;</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token property">&quot;build&quot;</span><span class="token operator">:</span> <span class="token string">&quot;node build/build.js&quot;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><p>这样，我们的<code>process.argv</code>前两个参数分别是<code>node</code>和<code>build/build.js</code>，故我们需要先去掉前面两个参数，才能获取剩余页面参数：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">var</span> ora <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">&quot;ora&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> rm <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">&quot;rimraf&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> path <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">&quot;path&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> utils <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">&quot;./utils&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> chalk <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">&quot;chalk&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> webpack <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">&quot;webpack&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> webpackConfig <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">&quot;./webpack.config&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> HtmlWebpackPlugin <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">&quot;html-webpack-plugin&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">var</span> entries <span class="token operator">=</span> utils<span class="token punctuation">.</span>entries<span class="token punctuation">;</span>
<span class="token keyword">var</span> pageArray<span class="token punctuation">;</span>

<span class="token comment">// 取掉前两个参数，分别为node和build</span>
process<span class="token punctuation">.</span>argv<span class="token punctuation">.</span><span class="token function">splice</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">if</span> <span class="token punctuation">(</span>process<span class="token punctuation">.</span>argv<span class="token punctuation">.</span>length<span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token comment">// 若传入页面参数，则单页面打包</span>
  pageArray <span class="token operator">=</span> process<span class="token punctuation">.</span>argv<span class="token punctuation">;</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
  <span class="token comment">// 若无传入页面参数，则全块打包</span>
  pageArray <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">keys</span><span class="token punctuation">(</span>entries<span class="token punctuation">)</span><span class="token punctuation">;</span>
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>pageArray<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token comment">// 开始输出loading状态</span>
<span class="token keyword">var</span> spinner <span class="token operator">=</span> <span class="token function">ora</span><span class="token punctuation">(</span><span class="token string">&quot;building for production...\n&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
spinner<span class="token punctuation">.</span><span class="token function">start</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

pageArray<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">val<span class="token punctuation">,</span> index<span class="token punctuation">,</span> array</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token function">rm</span><span class="token punctuation">(</span>path<span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span>__dirname<span class="token punctuation">,</span> <span class="token string">&quot;..&quot;</span><span class="token punctuation">,</span> <span class="token string">&quot;dist&quot;</span><span class="token punctuation">,</span> val<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token parameter">err</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token keyword">throw</span> err<span class="token punctuation">;</span>
    <span class="token comment">// print pageName[]</span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>index <span class="token operator">+</span> <span class="token string">&quot;: &quot;</span> <span class="token operator">+</span> val<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 输出目录dist/pageName</span>
    webpackConfig<span class="token punctuation">.</span>output<span class="token punctuation">.</span>path <span class="token operator">=</span> path<span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span>__dirname<span class="token punctuation">,</span> <span class="token string">&quot;..&quot;</span><span class="token punctuation">,</span> <span class="token string">&quot;dist&quot;</span><span class="token punctuation">,</span> val<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 入口文件设定为指定页面的入口文件</span>
    <span class="token comment">// main.js这里为通用入口文件</span>
    webpackConfig<span class="token punctuation">.</span>entry <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
    webpackConfig<span class="token punctuation">.</span>entry<span class="token punctuation">[</span>index<span class="token punctuation">]</span> <span class="token operator">=</span> path<span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span>
      __dirname<span class="token punctuation">,</span>
      <span class="token string">&quot;..&quot;</span><span class="token punctuation">,</span>
      <span class="token string">&quot;src&quot;</span><span class="token punctuation">,</span>
      <span class="token string">&quot;pages&quot;</span><span class="token punctuation">,</span>
      val<span class="token punctuation">,</span>
      <span class="token string">&quot;main.js&quot;</span>
    <span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 添加index.html主文件</span>
    webpackConfig<span class="token punctuation">.</span>plugins <span class="token operator">=</span> <span class="token punctuation">[</span>
      <span class="token keyword">new</span> <span class="token class-name">HtmlWebpackPlugin</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
        <span class="token comment">// 生成出来的html文件名</span>
        <span class="token literal-property property">filename</span><span class="token operator">:</span> <span class="token string">&quot;index.html&quot;</span><span class="token punctuation">,</span>
        <span class="token comment">// 每个html的模版，这里多个页面使用同一个模版</span>
        <span class="token literal-property property">template</span><span class="token operator">:</span> <span class="token string">&quot;./index.html&quot;</span><span class="token punctuation">,</span>
        <span class="token comment">// 或使用单独的模版</span>
        <span class="token comment">// template: './src/' + val + '/index.html',</span>
        <span class="token comment">// 自动将引用插入html</span>
        <span class="token literal-property property">inject</span><span class="token operator">:</span> <span class="token boolean">true</span>
        <span class="token comment">// 每个html引用的js模块，也可以在这里加上vendor等公用模块</span>
        <span class="token comment">// chunks: [name]</span>
      <span class="token punctuation">}</span><span class="token punctuation">)</span>
    <span class="token punctuation">]</span><span class="token punctuation">;</span>
    <span class="token comment">// 开启打包</span>
    <span class="token function">webpack</span><span class="token punctuation">(</span>webpackConfig<span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">err<span class="token punctuation">,</span> stats</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      spinner<span class="token punctuation">.</span><span class="token function">stop</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

      <span class="token comment">// 输出错误信息</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token keyword">throw</span> err<span class="token punctuation">;</span>

      <span class="token comment">// 输出打包完成信息</span>
      process<span class="token punctuation">.</span>stdout<span class="token punctuation">.</span><span class="token function">write</span><span class="token punctuation">(</span>
        stats<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
          <span class="token literal-property property">colors</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
          <span class="token literal-property property">modules</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
          <span class="token literal-property property">children</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
          <span class="token literal-property property">chunks</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
          <span class="token literal-property property">chunkModules</span><span class="token operator">:</span> <span class="token boolean">false</span>
        <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">&quot;\n\n&quot;</span>
      <span class="token punctuation">)</span><span class="token punctuation">;</span>

      console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>chalk<span class="token punctuation">.</span><span class="token function">cyan</span><span class="token punctuation">(</span><span class="token string">&quot;  Build complete.\n&quot;</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>
        chalk<span class="token punctuation">.</span><span class="token function">yellow</span><span class="token punctuation">(</span>
          <span class="token string">&quot;  Tip: built files are meant to be served over an HTTP server.\n&quot;</span> <span class="token operator">+</span>
            <span class="token string">&quot;  Opening index.html over file:// won't work.\n&quot;</span>
        <span class="token punctuation">)</span>
      <span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre></div><h3 id="_14-2-6-开发部署"><a href="#_14-2-6-开发部署" class="header-anchor">#</a> 14.2.6 开发部署</h3> <p>我们先讲解打包的实现，原因是开发部署的功能会比打包的能力要复杂一些，例如需要实现 watch 能力，以及实时路由匹配的能力等。</p> <h4 id="逻辑思路-2"><a href="#逻辑思路-2" class="header-anchor">#</a> 逻辑思路</h4> <p>开发环境的部署和生产环境不一致，我们规划的本地环境实现的效果如下：<br>
(1) 整个项目启动一次，多页面共享相同环境。<br>
(2) 根据路由来匹配不同页面，路由与页面目录一致。</p> <p>简单地说，可以理解为：</p> <ul><li>路由为<code>/page1</code>时，打开 page1 页面</li> <li>路由为<code>/page2</code>时，打开 page2 页面</li> <li>路由匹配不到对应页面时，进行相关提示</li></ul> <p>接下来，我们会用到 Express 模块。</p> <h4 id="express-模块与路由"><a href="#express-模块与路由" class="header-anchor">#</a> <a href="http://www.expressjs.com.cn/" target="_blank" rel="noopener noreferrer">Express 模块<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>与路由</h4> <p>Express 模块是一个基于 Node.js 平台的极简、灵活的 web 应用开发框架，它提供一系列强大的特性，帮助你创建各种 Web 应用。我们需要路由的匹配，这里我们使用 express 模块，首先我们需要了解下路由。</p> <p>路由（Routing）是由一个 URI（或者叫路径）和一个特定的 HTTP 方法（GET、POST 等）组成的，涉及到应用如何响应客户端对某个网站节点的访问。每一个路由都可以有一个或者多个处理器函数，当匹配到路由时，这个函数将被执行。</p> <p>路由的定义由如下结构组成：<code>app.METHOD(PATH, HANDLER)</code>。其中，<code>app</code>是一个 express 实例，<code>METHOD</code>是某个 HTTP 请求方式中的一个，<code>PATH</code>是服务器端的路径，<code>HANDLER</code>是当路由匹配到时需要执行的函数。</p> <p>我们来看一个官方示例：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// 对网站首页的访问返回 &quot;Hello World!&quot; 字样</span>
app<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">&quot;/&quot;</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">req<span class="token punctuation">,</span> res</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  res<span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span><span class="token string">&quot;Hello World!&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 网站首页接受 POST 请求</span>
app<span class="token punctuation">.</span><span class="token function">post</span><span class="token punctuation">(</span><span class="token string">&quot;/&quot;</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">req<span class="token punctuation">,</span> res</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  res<span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span><span class="token string">&quot;Got a POST request&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre></div><p><strong>(1) 请求对象（<code>req</code>）部分属性。</strong></p> <p>表 14-6 req 属性介绍</p> <table><thead><tr><th>属性</th> <th>介绍</th> <th>补充说明</th></tr></thead> <tbody><tr><td><code>req.params</code></td> <td>这是一个数组对象，命名过的参数会以键值对的形式存放</td> <td>比如有一个路由<code>/user/:name</code>，<code>&quot;name&quot;</code>属性会存放在<code>req.params.name</code>， 这个对象默认为<code>{}</code></td></tr> <tr><td><code>req.query</code></td> <td>一个解析过的请求参数对象，默认为<code>{}</code></td> <td>这个特性是<code>bodyParser()</code>中间件提供，其它的请求体解析中间件可以放在这个中间件之后。当<code>bodyParser()</code>中间件使用后，这个对象默认为<code>{}</code></td></tr> <tr><td><code>req.body</code></td> <td>这个对应的是解析过的请求体</td> <td>-</td></tr> <tr><td><code>req.route</code></td> <td>这个对象里是当前匹配的 Route 里包含的属性</td> <td>比如原始路径字符串，产生的正则，等等</td></tr> <tr><td><code>req.path</code></td> <td>返回请求的 URL 的路径名</td> <td>-</td></tr> <tr><td><code>req.host</code></td> <td>返回从&quot;Host&quot;请求头里取的主机名,不包含端口号</td> <td>-</td></tr></tbody></table> <p><strong>(2) 响应对象（<code>res</code>）部分属性。</strong></p> <p>表 14-7 res 属性介绍</p> <table><thead><tr><th>属性</th> <th>介绍</th></tr></thead> <tbody><tr><td><code>res.end()</code></td> <td>终结响应处理流程</td></tr> <tr><td><code>res.json()</code></td> <td>发送一个 JSON 格式的响应</td></tr> <tr><td><code>res.jsonp()</code></td> <td>发送一个支持 JSONP 的 JSON 格式的响应</td></tr> <tr><td><code>res.redirect()</code></td> <td>重定向请求</td></tr> <tr><td><code>res.render()</code></td> <td>渲染视图模板</td></tr> <tr><td><code>res.send()</code></td> <td>发送各种类型的响应</td></tr> <tr><td><code>res.sendFile</code></td> <td>以八位字节流的形式发送文件</td></tr> <tr><td><code>res.sendStatus()</code></td> <td>设置响应状态代码，并将其以字符串形式作为响应体的一部分发送</td></tr></tbody></table> <p>Express 的能力很强大，常常会用在服务端开发，包括常见的 HTTP 服务、Websocket 服务等，如果使用 Node.js 做类似聊天室等服务，Express 常常是主要选型方向之一，大家也可以多去了解一下。</p> <h4 id="代码实现-2"><a href="#代码实现-2" class="header-anchor">#</a> 代码实现</h4> <p>我们的开发部署代码放置在 build 文件夹下的 dev-server.js，则我们的 package.json 中的 script 设置如下：</p> <div class="language-json extra-class"><pre class="language-json"><code><span class="token punctuation">{</span>
  <span class="token property">&quot;scripts&quot;</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token property">&quot;dev&quot;</span><span class="token operator">:</span> <span class="token string">&quot;node build/dev-server.js&quot;</span><span class="token punctuation">,</span>
    <span class="token property">&quot;build&quot;</span><span class="token operator">:</span> <span class="token string">&quot;node build/build.js&quot;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><p>同时，我们将每个页面的主页面命名为[pageName].html，然后匹配路由之后就能获取相关页面：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// dev-server.js</span>
<span class="token keyword">var</span> path <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">&quot;path&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> express <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">&quot;express&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> utils <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">&quot;./utils&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> webpack <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">&quot;webpack&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> webpackConfig <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">&quot;./webpack.config&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> HtmlWebpackPlugin <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">&quot;html-webpack-plugin&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// Express实例</span>
<span class="token keyword">var</span> app <span class="token operator">=</span> <span class="token function">express</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 获取页面目录</span>
<span class="token keyword">var</span> entries <span class="token operator">=</span> utils<span class="token punctuation">.</span>entries<span class="token punctuation">;</span>

<span class="token comment">// 重置入口entry</span>
webpackConfig<span class="token punctuation">.</span>entry <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token comment">// 设置output为每个页面[name].js</span>
webpackConfig<span class="token punctuation">.</span>output<span class="token punctuation">.</span>filename <span class="token operator">=</span> <span class="token string">&quot;[name].js&quot;</span><span class="token punctuation">;</span>
webpackConfig<span class="token punctuation">.</span>output<span class="token punctuation">.</span>path <span class="token operator">=</span> path<span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span>__dirname<span class="token punctuation">,</span> <span class="token string">&quot;dist&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

Object<span class="token punctuation">.</span><span class="token function">keys</span><span class="token punctuation">(</span>entries<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">name</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token comment">// 每个页面生成一个entry，如果需要HotUpdate，在这里修改entry</span>
  webpackConfig<span class="token punctuation">.</span>entry<span class="token punctuation">[</span>name<span class="token punctuation">]</span> <span class="token operator">=</span> entries<span class="token punctuation">[</span>name<span class="token punctuation">]</span><span class="token punctuation">;</span>

  <span class="token comment">// 每个页面生成一个[name].html</span>
  <span class="token keyword">var</span> plugin <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HtmlWebpackPlugin</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
    <span class="token comment">// 生成出来的html文件名</span>
    <span class="token literal-property property">filename</span><span class="token operator">:</span> name <span class="token operator">+</span> <span class="token string">&quot;.html&quot;</span><span class="token punctuation">,</span>
    <span class="token comment">// 每个html的模版，这里多个页面使用同一个模版</span>
    <span class="token literal-property property">template</span><span class="token operator">:</span> <span class="token string">&quot;./index.html&quot;</span><span class="token punctuation">,</span>
    <span class="token comment">// 自动将引用插入html</span>
    <span class="token literal-property property">inject</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
    <span class="token comment">// 每个html引用的js模块，也可以在这里加上vendor等公用模块</span>
    <span class="token literal-property property">chunks</span><span class="token operator">:</span> <span class="token punctuation">[</span>name<span class="token punctuation">]</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  webpackConfig<span class="token punctuation">.</span>plugins<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>plugin<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// webpack编译器</span>
<span class="token keyword">var</span> compiler <span class="token operator">=</span> <span class="token function">webpack</span><span class="token punctuation">(</span>webpackConfig<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// webpack-dev-server中间件</span>
<span class="token keyword">var</span> devMiddleware <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">&quot;webpack-dev-middleware&quot;</span><span class="token punctuation">)</span><span class="token punctuation">(</span>compiler<span class="token punctuation">,</span> <span class="token punctuation">{</span>
  <span class="token literal-property property">publicPath</span><span class="token operator">:</span> <span class="token string">&quot;/&quot;</span><span class="token punctuation">,</span>
  <span class="token literal-property property">stats</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token literal-property property">colors</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
    <span class="token literal-property property">chunks</span><span class="token operator">:</span> <span class="token boolean">false</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token literal-property property">progress</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
  <span class="token literal-property property">inline</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
  <span class="token literal-property property">hot</span><span class="token operator">:</span> <span class="token boolean">true</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 使用webpack中间件</span>
app<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span>devMiddleware<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 路由</span>
app<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">&quot;/:pagename?&quot;</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">req<span class="token punctuation">,</span> res<span class="token punctuation">,</span> next</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">var</span> pagename <span class="token operator">=</span> req<span class="token punctuation">.</span>params<span class="token punctuation">.</span>pagename
    <span class="token operator">?</span> req<span class="token punctuation">.</span>params<span class="token punctuation">.</span>pagename <span class="token operator">+</span> <span class="token string">&quot;.html&quot;</span>
    <span class="token operator">:</span> <span class="token string">&quot;index.html&quot;</span><span class="token punctuation">;</span>

  <span class="token keyword">var</span> filepath <span class="token operator">=</span> path<span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span>compiler<span class="token punctuation">.</span>outputPath<span class="token punctuation">,</span> pagename<span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token comment">// 使用webpack提供的outputFileSystem</span>
  compiler<span class="token punctuation">.</span>outputFileSystem<span class="token punctuation">.</span><span class="token function">readFile</span><span class="token punctuation">(</span>filepath<span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">err<span class="token punctuation">,</span> result</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token comment">// something error</span>
      <span class="token keyword">return</span> <span class="token function">next</span><span class="token punctuation">(</span>
        <span class="token string">&quot;输入路径无效，请输入目录名作为路径，有效路径有：\n/&quot;</span> <span class="token operator">+</span>
          Object<span class="token punctuation">.</span><span class="token function">keys</span><span class="token punctuation">(</span>entries<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span><span class="token string">&quot;\n/&quot;</span><span class="token punctuation">)</span>
      <span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    <span class="token comment">// 发送获取到的页面</span>
    res<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string">&quot;content-type&quot;</span><span class="token punctuation">,</span> <span class="token string">&quot;text/html&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    res<span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span>result<span class="token punctuation">)</span><span class="token punctuation">;</span>
    res<span class="token punctuation">.</span><span class="token function">end</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

module<span class="token punctuation">.</span>exports <span class="token operator">=</span> app<span class="token punctuation">.</span><span class="token function">listen</span><span class="token punctuation">(</span><span class="token number">8080</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">err</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// do something</span>
    <span class="token keyword">return</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">&quot;Listening at http://localhost:8080\n&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre></div><p>这里面我们使用到 webpack-dev-middleware 模块，主要用于监视文件变化后重新编译，但这里并没有结合热加载来刷新页面，我们需要再添加一些 Webpack 插件来辅助实现。</p> <h3 id="_14-2-7-webpack-插件"><a href="#_14-2-7-webpack-插件" class="header-anchor">#</a> 14.2.7 Webpack 插件</h3> <p>Webpack 插件也在<a href="/vue-ebook/vue-ebook/2.html">《第2章 Vue 环境快速搭建》</a>进行过一些基本介绍，这里我们就直接介绍一些会用到的插件吧。</p> <h4 id="express-与-webpack"><a href="#express-与-webpack" class="header-anchor">#</a> Express 与 Webpack</h4> <p>Express 本质是一系列 middleware 的集合，在这里比较适合 Express 的 Webpack 插件是 webpack-dev-middleware 和 webpack-hot-middleware，我们分别来介绍一下。</p> <h4 id="webpack-dev-middleware"><a href="#webpack-dev-middleware" class="header-anchor">#</a> <a href="https://www.npmjs.com/package/webpack-dev-middleware" target="_blank" rel="noopener noreferrer">webpack-dev-middleware<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></h4> <p>webpack-dev-middleware 是一个处理静态资源的 middleware。有时候我们无需使用到 Express，我们常常使用 webpack-dev-server 开启动服务。
webpack-dev-server 实际上是一个小型 Express 服务器，它也是用 webpack-dev-middleware 来处理 Webpack 编译后的输出。</p> <h4 id="webpack-hot-middleware"><a href="#webpack-hot-middleware" class="header-anchor">#</a> <a href="https://www.npmjs.com/package/webpack-hot-middleware" target="_blank" rel="noopener noreferrer">webpack-hot-middleware<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></h4> <p>webpack-hot-middleware 是一个结合 webpack-dev-middleware 使用的 middleware，它可以实现浏览器的无刷新更新（hot reload）。这也是 Webpack 文档里常说的 HMR（Hot Module Replacement）。</p> <h4 id="实现热加载和页面刷新"><a href="#实现热加载和页面刷新" class="header-anchor">#</a> 实现热加载和页面刷新</h4> <p>其实如果将热加载定义为文件变动时重新编译的话，其实我们前面已经完成了。但热加载的功能，完整的使用方法需要搭配页面自动刷新。因此，我们需要调整三个地方：<br>
(1) 每个页面入口需要添加<code>webpack-hot-middleware/client?reload=true</code>。<br>
(2) 在 Webpack 配置中添加 plugin 插件<code>new webpack.HotModuleReplacementPlugin()</code>。<br>
(3) 在 Express 实例中添加中间件<code>'webpack-hot-middleware'</code>。</p> <p>我们的代码需要调整为：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// dev-server.js</span>
<span class="token comment">// 这里只注释介绍调整的部分，其他注释内容可参考前面的介绍</span>
<span class="token keyword">var</span> path <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">&quot;path&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> express <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">&quot;express&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> utils <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">&quot;./utils&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> webpack <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">&quot;webpack&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> webpackConfig <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">&quot;./webpack.config&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> HtmlWebpackPlugin <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">&quot;html-webpack-plugin&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 新增以下插件</span>
<span class="token keyword">var</span> WebpackDevMiddleware <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">&quot;webpack-dev-middleware&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> WebpackHotMiddleware <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">&quot;webpack-hot-middleware&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">var</span> app <span class="token operator">=</span> <span class="token function">express</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> entries <span class="token operator">=</span> utils<span class="token punctuation">.</span>entries<span class="token punctuation">;</span>
<span class="token comment">// entry中添加HotUpdate地址</span>
<span class="token keyword">var</span> hotMiddlewareScript <span class="token operator">=</span> <span class="token string">&quot;webpack-hot-middleware/client?reload=true&quot;</span><span class="token punctuation">;</span>

webpackConfig<span class="token punctuation">.</span>entry <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
webpackConfig<span class="token punctuation">.</span>output<span class="token punctuation">.</span>filename <span class="token operator">=</span> <span class="token string">&quot;[name].js&quot;</span><span class="token punctuation">;</span>
webpackConfig<span class="token punctuation">.</span>output<span class="token punctuation">.</span>path <span class="token operator">=</span> path<span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span>__dirname<span class="token punctuation">,</span> <span class="token string">&quot;dist&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

Object<span class="token punctuation">.</span><span class="token function">keys</span><span class="token punctuation">(</span>entries<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">name</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token comment">// 每个页面生成一个entry</span>
  <span class="token comment">// 这里修改entry实现HotUpdate</span>
  webpackConfig<span class="token punctuation">.</span>entry<span class="token punctuation">[</span>name<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span>entries<span class="token punctuation">[</span>name<span class="token punctuation">]</span><span class="token punctuation">,</span> hotMiddlewareScript<span class="token punctuation">]</span><span class="token punctuation">;</span>

  <span class="token keyword">var</span> plugin <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HtmlWebpackPlugin</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
    <span class="token literal-property property">filename</span><span class="token operator">:</span> name <span class="token operator">+</span> <span class="token string">&quot;.html&quot;</span><span class="token punctuation">,</span>
    <span class="token literal-property property">template</span><span class="token operator">:</span> <span class="token string">&quot;./index.html&quot;</span><span class="token punctuation">,</span>
    <span class="token literal-property property">inject</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
    <span class="token literal-property property">chunks</span><span class="token operator">:</span> <span class="token punctuation">[</span>name<span class="token punctuation">]</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  webpackConfig<span class="token punctuation">.</span>plugins<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>plugin<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 添加热加载插件</span>
webpackConfig<span class="token punctuation">.</span>plugins<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">webpack<span class="token punctuation">.</span>HotModuleReplacementPlugin</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">var</span> compiler <span class="token operator">=</span> <span class="token function">webpack</span><span class="token punctuation">(</span>webpackConfig<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 添加两个插件中间件</span>
app<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span>
  <span class="token function">WebpackDevMiddleware</span><span class="token punctuation">(</span>compiler<span class="token punctuation">,</span> <span class="token punctuation">{</span>
    <span class="token literal-property property">publicPath</span><span class="token operator">:</span> <span class="token string">&quot;/&quot;</span><span class="token punctuation">,</span>
    <span class="token literal-property property">stats</span><span class="token operator">:</span> <span class="token punctuation">{</span>
      <span class="token literal-property property">colors</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
      <span class="token literal-property property">chunks</span><span class="token operator">:</span> <span class="token boolean">false</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token literal-property property">progress</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
    <span class="token literal-property property">inline</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
    <span class="token literal-property property">hot</span><span class="token operator">:</span> <span class="token boolean">true</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
app<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span><span class="token function">WebpackHotMiddleware</span><span class="token punctuation">(</span>compiler<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

app<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">&quot;/:pagename?&quot;</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">req<span class="token punctuation">,</span> res<span class="token punctuation">,</span> next</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token comment">// 这里没有调整，篇幅原因省略</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

module<span class="token punctuation">.</span>exports <span class="token operator">=</span> app<span class="token punctuation">.</span><span class="token function">listen</span><span class="token punctuation">(</span><span class="token number">8080</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">err</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token comment">// 这里同样的没有调整，篇幅原因省略</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre></div><p>这样，我们就实现了代码的热加载以及页面自动刷新了。</p> <p>除了基本的编译构建、打包部署等能力，我们还需要配备更多的选型能力，例如是否生成、如何生成 Source Map，同时还有代码压缩、hash 命名等能力的提供，其实大家也可以去 Webpack 官方看一下，官方也有推荐的配置、插件和相关的使用方式介绍，这里也不多讲啦，讲多了就从 Vue 的内容变成了 Webpack 的内容了。</p> <p>说了那么多，我们来看一下最终的效果，首先是目录结构如图 14-8 ：<br> <img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/vue-14-5.jpg" alt="image"><br>
图 14-1 目录结构</p> <p>本地构建效果如图 14-9：<br> <img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/vue-14-1.jpg" alt="image"><br>
图 14-2 本地构建效果</p> <p>本地构建页面效果如图 14-10：<br> <img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/vue-14-10.jpg" alt="image"><br>
图 14-3 本地构建页面效果</p> <p>我们也可以指定打包某些页面：<br> <img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/vue-14-1.jpg" alt="image"><br>
图 14-4 指定打包某些页面效果</p> <p>如果不带参数，则默认打包全部页面：<br> <img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/vue-14-3.jpg" alt="image"><br>
图 14-5 默认打包全部页面</p> <blockquote><p><a href="https://github.com/godbasin/vue-ebook/tree/vue-sourcecode/14/1-webpack-multi-apps" target="_blank" rel="noopener noreferrer">点击此处查看源码<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></p></blockquote> <h2 id="_14-3-vue-cli-脚手架配置"><a href="#_14-3-vue-cli-脚手架配置" class="header-anchor">#</a> 14.3 Vue CLI 脚手架配置</h2> <p>上面讲的是使用 Webpack 自行实现多页应用的功能，而前面[《第2章 Vue 环境快速搭建》(./2.md)中我们介绍了其实官方的脚手架 Vue CLI 也是使用 Webpack，那么我们是否可以基于原有脚手架基础下进行调整，来实现同样的能力呢？</p> <h3 id="_14-3-1-pages-选项"><a href="#_14-3-1-pages-选项" class="header-anchor">#</a> 14.3.1 pages 选项</h3> <p>在 Vue CLI 的配置选项中，提供了 pages 选项，在多页模式下构建应用。同样的，每个“page”应该有一个对应的 JavaScript 入口文件。其值应该是一个对象，对象的<code>key</code>是入口的名字，<code>value</code>支持两种模式，分别是：<br>
(1) 一个指定了<code>entry</code>, <code>template</code>, <code>filename</code>, <code>title</code>和<code>chunks</code>的对象 (除了<code>entry</code>之外都是可选的)。<br>
(2) 一个指定其<code>entry</code>的字符串。</p> <p>所以，其实前面写的一大堆 Webpack 功能，我们只需要这样配置就可以了：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// vue.config.js</span>
module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span>
  <span class="token comment">// 其他选项</span>
  <span class="token literal-property property">pages</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token literal-property property">page1</span><span class="token operator">:</span> <span class="token string">&quot;src/pages/page1/main.js&quot;</span><span class="token punctuation">,</span>
    <span class="token literal-property property">page2</span><span class="token operator">:</span> <span class="token string">&quot;src/pages/page2/main.js&quot;</span><span class="token punctuation">,</span>
    <span class="token literal-property property">page3</span><span class="token operator">:</span> <span class="token string">&quot;src/pages/page3/main.js&quot;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre></div><p>我们可以使用脚手架原配的命令来运行本地构建：<br> <img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/vue-14-6.png" alt="image"><br>
图 14-6 脚手架本地构建</p> <p>同样地，也可以进行打包：<br> <img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/vue-14-7.jpg" alt="image"><br>
图 14-7 脚手架打包</p> <h3 id="_14-3-2-自动生成-pages-路径"><a href="#_14-3-2-自动生成-pages-路径" class="header-anchor">#</a> 14.3.2 自动生成 pages 路径</h3> <p>如果是使用上面的方式进行配置，每次我们变更页面名字、新增或是删除页面的时候，都需要手动进行更新，这我们可以同样地使用前面的路径获取来进行调整：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// vue.config.js</span>
<span class="token keyword">var</span> glob <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">&quot;glob&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">function</span> <span class="token function">getEntries</span><span class="token punctuation">(</span><span class="token parameter">globPath</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token comment">// 获取所有匹配文件的文件名数组</span>
  <span class="token keyword">var</span> files <span class="token operator">=</span> glob<span class="token punctuation">.</span><span class="token function">sync</span><span class="token punctuation">(</span>globPath<span class="token punctuation">)</span><span class="token punctuation">,</span>
    entries <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>

  files<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">filepath</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// 取倒数第二层(view下面的文件夹)做包名</span>
    <span class="token keyword">var</span> split <span class="token operator">=</span> filepath<span class="token punctuation">.</span><span class="token function">split</span><span class="token punctuation">(</span><span class="token string">&quot;/&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">var</span> name <span class="token operator">=</span> split<span class="token punctuation">[</span>split<span class="token punctuation">.</span>length <span class="token operator">-</span> <span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">;</span>

    <span class="token comment">// 保存{'目录名': '目录路径'}</span>
    entries<span class="token punctuation">[</span>name<span class="token punctuation">]</span> <span class="token operator">=</span> filepath<span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">return</span> entries<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">// 获取所有匹配src下目录的文件夹名字，其中文件夹里main.js为页面入口</span>
<span class="token keyword">var</span> pages <span class="token operator">=</span> <span class="token function">getEntries</span><span class="token punctuation">(</span><span class="token string">&quot;src/**/main.js&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>pages<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 打印看看</span>

module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span>
  <span class="token comment">// 其他选项</span>
  <span class="token comment">// pages 选项</span>
  <span class="token literal-property property">pages</span><span class="token operator">:</span> pages
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre></div><p>这样我们就可以自动查找对应的页面内容，然后生成对应的页面了：<br> <img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/vue-14-8.jpg" alt="image"><br>
图 14-8 自动生成对应的页面</p> <p>页面效果，和前面 Webpack 实现的效果一致：<br> <img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/vue-14-10.jpg" alt="image"><br>
图 14-9 页面效果</p> <p>显然，使用官方脚手架 Vue CLI 能节省不少的时间和力气，只需要短短的几行配置就能实现使用 Webpack 时费尽力气做出来的效果。但是这样是否意味着就不需要了解前面的内容呢？并不是这样的，其实我们也是在了解前面的方案之后，才能快速地进行自动生成 pages 路径这样的操作。而当我们遇到需要更加自定义实现的效果时，官方脚手架或许不再能简单进行支持，这时候掌握了 Webpack 方式的你就能快速地进行调整和实现。</p> <p>工具使得做事情更快快速便捷，但并不意味着我们可以直接放弃更深层次的能力，只有在掌握工具的实现方式之后，才能更加自由地使用它。这里使用 Vue CLI 脚手架来进行多页开发，我们最终打包的页面结构是这样的：</p> <p><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/vue-14-9.jpg" alt="image"><br>
图 14-10 打包后页面结构</p> <blockquote><p><a href="https://github.com/godbasin/vue-ebook/tree/vue-sourcecode/14/2-vue-cli-multi-apps" target="_blank" rel="noopener noreferrer">点击此处查看源码<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></p></blockquote> <p>如果现在你需要调整 Vue CLI 的配置，来实现上方 Webpack 所实现的效果（也就是每个页面作为单独的应用生成文件夹进行打包），你要怎么来做呢？</p></div> <!----> <footer class="page-edit"><div class="edit-link"><a href="https://github.com/godbasin/vue-ebook/edit/ebook-sourcecode/docs/vue-ebook/14.md" target="_blank" rel="noopener noreferrer">帮阿猪改善此页面！</a> <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></div> <!----> <div style="margin-top:30px;"><div class="el-row" style="margin-left:-10px;margin-right:-10px;"><div class="el-col el-col-24 el-col-sm-0 el-col-md-2 el-col-lg-4" style="padding-left:10px;padding-right:10px;display:block;"><div style="width:1px;height:1px;"></div></div> <div class="el-col el-col-24 el-col-sm-24 el-col-md-18 el-col-lg-16" style="padding-left:10px;padding-right:10px;"><div class="el-card box-card is-always-shadow"><div class="el-card__header"><div class="clearfix"><span>温馨提示喵</span></div></div><div class="el-card__body"> <div class="el-row" style="margin-left:-10px;margin-right:-10px;"><div class="el-col el-col-24 el-col-xs-24 el-col-sm-12" style="padding-left:10px;padding-right:10px;"><div class="el-image"><div class="image-slot"><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/assets/img/loading.gif" style="width:100%;"></div><!----></div></div> <div class="el-col el-col-24 el-col-xs-24 el-col-sm-12" style="padding-left:10px;padding-right:10px;"><div class="copyright-text"><div>本书采用“保持署名—非商用”<a href="http://creativecommons.org/licenses/by-nc/4.0/" target="_blank">创意共享4.0许可证</a>。只要保持原作者署名和非商用，您可以自由地阅读、分享、修改本书。</div> <div>作者：<a href="https://github.com/godbasin" target="_blank">被删</a></div></div></div></div></div></div></div></div></div></footer> <div class="page-nav"><p class="inner"><span class="prev">
        ←
        <a href="/vue-ebook/vue-ebook/13.html" class="prev">
          第13章 实战：表单配置化实现
        </a></span> <span class="next"><a href="/vue-ebook/vue-ebook/15.html">
          第15章 Vue 周边拓展
        </a>
        →
      </span></p></div>  <div class="gitalk-container theme-default-content"><div id="gitalk-container" class="content"></div></div></main> <div id="kitty-container"><span><div role="tooltip" id="el-popover-8510" aria-hidden="true" class="el-popover el-popper" style="width:undefinedpx;display:none;"><!----><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/2code2.jpg" class="image"> <div class="text">牡羊猪的猫粮罐</div> </div><span class="el-popover__reference-wrapper"><div id="kitty" style="background:url(https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/assets/img/kitty2.svg);"></div></span></span> <div class="el-dialog__wrapper" style="display:none;"><div role="dialog" aria-modal="true" aria-label="牡羊猪是这样渐渐胖成猪的喵（点击图片可以切换噢）" class="el-dialog" style="margin-top:15vh;"><div class="el-dialog__header"><span class="el-dialog__title">牡羊猪是这样渐渐胖成猪的喵（点击图片可以切换噢）</span><button type="button" aria-label="Close" class="el-dialog__headerbtn"><i class="el-dialog__close el-icon el-icon-close"></i></button></div><!----><!----></div></div></div></div><div class="global-ui"></div></div>
    <script src="/vue-ebook/assets/js/app.a856ee15.js" defer></script><script src="/vue-ebook/assets/js/2.2a4d101b.js" defer></script><script src="/vue-ebook/assets/js/3.9083e877.js" defer></script><script src="/vue-ebook/assets/js/13.8d233329.js" defer></script>
  </body>
</html>
